package com.code.wflow.utils;

import com.alibaba.fastjson2.JSON;
import com.code.wflow.bo.WorkflowNodeBO;
import com.code.wflow.common.Constants;
import com.code.wflow.constants.NodeTypeEnum;
import com.code.wflow.model.WorkflowNode;
import com.code.wflow.node.NodeProperties;
import com.code.wflow.vo.WorkflowNodeVO;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ggh
 * @date 2022/10/26
 * 流程解析
 */
public class WorkflowUtils {

    /**
     * 解析流程定义
     *
     * @param workflowDef
     * @return
     */
    public static List<WorkflowNode> parseWorkflowDef(String workflowDef) {
        if (StringUtils.isEmpty(workflowDef)) {
            return null;
        }

        WorkflowNodeBO workflowNodeBo = JSON.parseObject(workflowDef, WorkflowNodeBO.class);

        List<WorkflowNode> nodeList = new ArrayList<>(1);
        addWorkflow(workflowNodeBo, Constants.ROOT_LEVEL, Constants.NULL_CONDITION_LEVEL, nodeList);
        return nodeList;
    }

    /**
     * 构建流程定义
     *
     * @param workflowNodeList
     * @return
     */
    public static String buildWorkflowDef(List<WorkflowNode> workflowNodeList) {
        if (CollectionUtils.isEmpty(workflowNodeList)) {
            return null;
        }
        List<WorkflowNodeVO> workflowNodeVOList = nodeToVO(workflowNodeList);

        WorkflowNodeVO rootVo = workflowNodeVOList.stream()
                .filter((vo) -> vo.getParentId() == null)
                .findFirst()
                .get();

        buildChildenNew(rootVo, workflowNodeVOList);

        return JSON.toJSONString(rootVo);
    }

    private static void addWorkflow(WorkflowNodeBO workflowNodeBo, Integer level, Integer conditionsLevel, List<WorkflowNode> nodeList) {
        WorkflowNode node = buildWorkflowFormBO(workflowNodeBo);
        node.setLevel(level);
        node.setConditionsLevel(conditionsLevel);
        nodeList.add(node);

        WorkflowNodeBO children = workflowNodeBo.getChildren();
        List<WorkflowNodeBO> branchs = workflowNodeBo.getBranchs();
        if (children != null && !StringUtils.isEmpty(children.getId())) {
            addWorkflow(children, level + 1, conditionsLevel, nodeList);
        } else {
            node.setEnd(true);
        }
        if (!CollectionUtils.isEmpty(branchs)) {
            conditionsLevel = node.getLevel();
            addWorkflowBranchs(branchs, level + 1, conditionsLevel, nodeList);
        }
    }

    private static void addWorkflowBranchs(List<WorkflowNodeBO> branchs, Integer level, Integer conditionsLevel, List<WorkflowNode> nodeList) {
        for (WorkflowNodeBO workflowNodeBO : branchs) {
            addWorkflow(workflowNodeBO, level, conditionsLevel, nodeList);
        }
    }


    private static WorkflowNode buildWorkflowFormBO(WorkflowNodeBO workflowNodeBo) {
        WorkflowNode node = BeanCopierUtils.copy(workflowNodeBo, WorkflowNode.class);
        node.setCode(workflowNodeBo.getId());
        node.setParentCode(workflowNodeBo.getParentId());
        return node;
    }

    private static void buildChilden(WorkflowNodeVO parentNodeVo, List<WorkflowNodeVO> workflowNodeVOList) {
        workflowNodeVOList.stream().forEach((vo) -> {
            if (!StringUtils.isEmpty(vo.getParentId()) && vo.getParentId().equals(parentNodeVo.getId())) {
                buildChilden(vo, workflowNodeVOList);
                parentNodeVo.setChildren(vo);
            }
        });
    }

    private static void buildChildenNew(WorkflowNodeVO parentNodeVo, List<WorkflowNodeVO> workflowNodeVOList) {
        workflowNodeVOList.stream().forEach((vo) -> {
            if (!StringUtils.isEmpty(vo.getParentId()) && vo.getParentId().equals(parentNodeVo.getId())) {
                buildChildenNew(vo, workflowNodeVOList);
                parentNodeVo.setChildren(vo);
            }
        });
    }

    private static List<WorkflowNodeVO> nodeToVO(List<WorkflowNode> nodeList) {
        List<WorkflowNodeVO> nodeVoList = new ArrayList<>(nodeList.size());
        for (WorkflowNode node : nodeList) {
            WorkflowNodeVO nodeVO = new WorkflowNodeVO();

            nodeVO.setId(node.getCode());
            nodeVO.setName(node.getName());
            nodeVO.setType(node.getType());
            nodeVO.setDesc(node.getDesc());
            nodeVO.setParentId(node.getParentCode());

            NodeTypeEnum nodeTypeEnum = NodeTypeEnum.parse(node.getType());
            NodeProperties nodeProperties = JSON.parseObject(node.getProps(), nodeTypeEnum.getCls());
            nodeVO.setProps(nodeProperties);
            nodeVoList.add(nodeVO);
        }

        return nodeVoList;
    }

}
